/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance;

import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.FieldAccessor;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.implementation.SuperMethodCall;
import net.bytebuddy.implementation.bind.annotation.Morph;
import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.matcher.ElementMatchers;
import org.apache.skywalking.apm.agent.core.logging.api.ILog;
import org.apache.skywalking.apm.agent.core.logging.api.LogManager;
import org.apache.skywalking.apm.agent.core.plugin.AbstractClassEnhancePluginDefine;
import org.apache.skywalking.apm.agent.core.plugin.EnhanceContext;
import org.apache.skywalking.apm.agent.core.plugin.PluginException;
import org.apache.skywalking.apm.agent.core.plugin.bootstrap.BootstrapInstrumentBoost;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.DeclaredInstanceMethodsInterceptPoint;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.EnhanceException;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint;
import org.apache.skywalking.apm.agent.core.plugin.interceptor.StaticMethodsInterceptPoint;
import org.apache.skywalking.apm.util.StringUtil;

import static net.bytebuddy.jar.asm.Opcodes.ACC_PRIVATE;
import static net.bytebuddy.jar.asm.Opcodes.ACC_VOLATILE;
import static net.bytebuddy.matcher.ElementMatchers.isStatic;
import static net.bytebuddy.matcher.ElementMatchers.not;

/**
 * 继承了底层的插件增强抽象类。这个类控制所有的增强行为，包含增强constructors、methods、static methods。
 * 所有增强都是基于三个拦截器类型拦截点(ConstructorInterceptPoint、InstanceMethodsInterceptPoint、StaticMethodsInterceptPoint)。
 *
 * This class controls all enhance operations, including enhance constructors, instance methods and static methods. All
 * the enhances base on three types interceptor point: {@link ConstructorInterceptPoint}, {@link
 * InstanceMethodsInterceptPoint} and {@link StaticMethodsInterceptPoint} If plugin is going to enhance constructors,
 * instance methods, or both, {@link ClassEnhancePluginDefine} will add a field of {@link Object} type.
 */
public abstract class ClassEnhancePluginDefine extends AbstractClassEnhancePluginDefine {
    private static final ILog LOGGER = LogManager.getLogger(ClassEnhancePluginDefine.class);

    /**
     * New field name.
     */
    public static final String CONTEXT_ATTR_NAME = "_$EnhancedClassField_ws";

    /**
     * 定义怎么增强一个类。
     * Begin to define how to enhance class. After invoke this method, only means definition is finished.
     *
     * @param typeDescription 目标类描述 target class description
     * @param newClassBuilder  用于操作字节码的byteBuddy构建器 byte-buddy's builder to manipulate class bytecode.
     * @return new byte-buddy's builder for further manipulation.
     */
    @Override
    protected DynamicType.Builder<?> enhance(TypeDescription typeDescription, DynamicType.Builder<?> newClassBuilder,
        ClassLoader classLoader, EnhanceContext context) throws PluginException {
        // 增强静态方法（增强静态类），增强实例方法
        newClassBuilder = this.enhanceClass(typeDescription, newClassBuilder, classLoader);
        newClassBuilder = this.enhanceInstance(typeDescription, newClassBuilder, classLoader, context);

        return newClassBuilder;
    }

    /**
     * 通过拦截类对实例进行增强
     * Enhance a class to intercept constructors and class instance methods.
     *
     * @param typeDescription 目标类型描述 target class description
     * @param newClassBuilder 操作字节码的构造器 byte-buddy's builder to manipulate class bytecode.
     * @return new byte-buddy's builder for further manipulation.
     */
    private DynamicType.Builder<?> enhanceInstance(TypeDescription typeDescription,
        DynamicType.Builder<?> newClassBuilder, ClassLoader classLoader,
        EnhanceContext context) throws PluginException {
        // 获取实例拦截类，这个是真多。。比类拦截类多。好几十个了
        ConstructorInterceptPoint[] constructorInterceptPoints = getConstructorsInterceptPoints();
        // 拦截方法的
        InstanceMethodsInterceptPoint[] instanceMethodsInterceptPoints = getInstanceMethodsInterceptPoints();
        // 描述类型
        String enhanceOriginClassName = typeDescription.getTypeName();
        // 实际上，肯定存在构造器拦截、方法拦截的。都当他们是true
        boolean existedConstructorInterceptPoint = false;
        if (constructorInterceptPoints != null && constructorInterceptPoints.length > 0) {
            existedConstructorInterceptPoint = true;
        }
        boolean existedMethodsInterceptPoints = false;
        if (instanceMethodsInterceptPoints != null && instanceMethodsInterceptPoints.length > 0) {
            existedMethodsInterceptPoints = true;
        }

        /**
         * nothing need to be enhanced in class instance, maybe need enhance static methods.
         */
        if (!existedConstructorInterceptPoint && !existedMethodsInterceptPoints) {
            return newClassBuilder;
        }

        /**
         * 操作class源码
         * Manipulate class source code.<br/>
         *
         * 第一步：添加字段 CONTEXT_ATTR_NAME=_$EnhancedClassField_ws===================================
         *
         * new class need:<br/> 一个class需要
         * 1.Add field, name {@link #CONTEXT_ATTR_NAME}. 添加字段 CONTEXT_ATTR_NAME=_$EnhancedClassField_ws
         * 2.Add a field accessor for this field. 添加访问字段CONTEXT_ATTR_NAME的方法
         *
         * 并且确保源码操作只会发生一次
         * And make sure the source codes manipulation only occurs once.
         *
         */
        if (!typeDescription.isAssignableTo(EnhancedInstance.class)) {
            // 判断对象未扩展，才进去处理
            if (!context.isObjectExtended()) {
                // 定义一个field字段
                newClassBuilder = newClassBuilder.defineField(
                    // 定义一个上下文字段名 _$EnhancedClassField_ws。
                    CONTEXT_ATTR_NAME,
                    // 类型为Object
                    Object.class,
                    // 私有 并且 volatile
                    ACC_PRIVATE | ACC_VOLATILE)
                    // 继承 EnhancedInstance
                     .implement(EnhancedInstance.class)
                     .intercept(FieldAccessor.ofField(CONTEXT_ATTR_NAME));
                // 标识已处理
                context.extendObjectCompleted();
            }
        }

        /**
         * 第二步：增强构造方法===================================
         * 2. enhance constructors
         */
        if (existedConstructorInterceptPoint) {
            // 遍历所有构造方法拦截类
            for (ConstructorInterceptPoint constructorInterceptPoint : constructorInterceptPoints) {
                if (isBootstrapInstrumentation()) {
                    newClassBuilder = newClassBuilder.constructor(constructorInterceptPoint.getConstructorMatcher())
                                                     .intercept(SuperMethodCall.INSTANCE.andThen(MethodDelegation.withDefaultConfiguration()
                                                                                                                 .to(BootstrapInstrumentBoost
                                                                                                                     .forInternalDelegateClass(constructorInterceptPoint
                                                                                                                         .getConstructorInterceptor()))));
                } else {
                    // 看这步，isBootstrapInstrumentation()默认就是false，会走到这里
                    newClassBuilder = newClassBuilder
                        // 给构造方法注入Matcher。
                        // - 举个例子：RedisClientInstrumentation 中返回 takesArgumentWithType(1, "io.lettuce.core.RedisURI")
                        .constructor(constructorInterceptPoint.getConstructorMatcher())
                        // - 拦截当前执行的实例
                        .intercept(SuperMethodCall.INSTANCE.andThen(MethodDelegation.withDefaultConfiguration()
                        // ConstructorInter，处理类(比如http访问链接的参数增强)
                        .to(new ConstructorInter(constructorInterceptPoint.getConstructorInterceptor(), classLoader))));
                }
            }
        }

        /**
         * 第三步：增强实例方法===================================
         * 3. enhance instance methods
         */
        if (existedMethodsInterceptPoints) {
            // 遍历所有实例方法拦截点进行增强
            for (InstanceMethodsInterceptPoint instanceMethodsInterceptPoint : instanceMethodsInterceptPoints) {
                String interceptor = instanceMethodsInterceptPoint.getMethodsInterceptor();
                if (StringUtil.isEmpty(interceptor)) {
                    throw new EnhanceException("no InstanceMethodsAroundInterceptor define to enhance class " + enhanceOriginClassName);
                }
                // 拦截非静态方法并且加入对应的matcher.
                // -- 举例：AbstractDriverInstrumentation
                ElementMatcher.Junction<MethodDescription> junction = not(isStatic()).and(instanceMethodsInterceptPoint.getMethodsMatcher());
                // 用于增强特殊的，如spring controller
                if (instanceMethodsInterceptPoint instanceof DeclaredInstanceMethodsInterceptPoint) {
                    junction = junction.and(ElementMatchers.<MethodDescription>isDeclaredBy(typeDescription));
                }
                if (instanceMethodsInterceptPoint.isOverrideArgs()) {
                    if (isBootstrapInstrumentation()) {
                        newClassBuilder = newClassBuilder
                                // 找到对应方法，上面matcher、not(isStatic())过滤可找到
                                .method(junction)
                                // 创建一个方法委派的默认定义
                                .intercept(MethodDelegation.withDefaultConfiguration()
                                    // 绑定一个方法委派的请求参数类型 OverrideCallable  （后面是表示原来方法调用，用法：overrideCallable.call()）
                                    .withBinders(Morph.Binder.install(OverrideCallable.class))
                                    .to(BootstrapInstrumentBoost.forInternalDelegateClass(interceptor)));
                    } else {
                        newClassBuilder = newClassBuilder
                                // 找到对应方法，上面matcher、not(isStatic())过滤可找到
                                .method(junction)
                                // 创建一个方法委派的默认定义
                                .intercept(MethodDelegation.withDefaultConfiguration()
                                        // 绑定一个方法委派的请求参数类型 OverrideCallable  （后面是表示原来方法调用，用法：overrideCallable.call()）
                                        .withBinders(Morph.Binder.install(OverrideCallable.class))
                                        .to(new InstMethodsInterWithOverrideArgs(interceptor, classLoader)));
                    }
                } else {
                    // 走到这步。isOverrideArgs == false
                    if (isBootstrapInstrumentation()) {
                        newClassBuilder = newClassBuilder
                                // 找到对应方法，上面matcher、not(isStatic())过滤可找到
                                .method(junction)
                                // 创建一个方法委派的默认定义
                                .intercept(MethodDelegation.withDefaultConfiguration()
                                        .to(BootstrapInstrumentBoost.forInternalDelegateClass(interceptor)));
                    } else {
                        newClassBuilder = newClassBuilder
                                // 找到对应方法，上面matcher、not(isStatic())过滤可找到
                                .method(junction)
                                // 创建一个方法委派的默认定义
                                .intercept(MethodDelegation.withDefaultConfiguration()
                                        .to(new InstMethodsInter(interceptor, classLoader)));
                    }
                }
            }
        }

        return newClassBuilder;
    }

    /**
     * 根据对应拦截器类增强类（增强静态类）
     * Enhance a class to intercept class static methods.
     *
     * @param typeDescription 目标类描述 target class description
     * @param newClassBuilder 用于操作字节码的byteBuddy构建器  byte-buddy's builder to manipulate class bytecode.
     * @return new byte-buddy's builder for further manipulation.
     */
    private DynamicType.Builder<?> enhanceClass(TypeDescription typeDescription, DynamicType.Builder<?> newClassBuilder,
        ClassLoader classLoader) throws PluginException {
        // 处理静态方法拦截点
        // -- 获取所有instrumentation **静态类**拦截类  增强类的描述明typeName
        StaticMethodsInterceptPoint[] staticMethodsInterceptPoints = getStaticMethodsInterceptPoints();
        String enhanceOriginClassName = typeDescription.getTypeName();
        if (staticMethodsInterceptPoints == null || staticMethodsInterceptPoints.length == 0) {
            return newClassBuilder;
        }
        // 具体所有拦截类的用法都需要view一下，这里遍历所有拦截类。这里拦截的都是静态类
        for (StaticMethodsInterceptPoint staticMethodsInterceptPoint : staticMethodsInterceptPoints) {
            String interceptor = staticMethodsInterceptPoint.getMethodsInterceptor();
            if (StringUtil.isEmpty(interceptor)) {
                throw new EnhanceException("no StaticMethodsAroundInterceptor define to enhance class " + enhanceOriginClassName);
            }
            // TODO：这个isOverrideArgs作用是什么？还没确定，先看着，毕竟所有实现类目前都是=false
            if (staticMethodsInterceptPoint.isOverrideArgs()) {
                if (isBootstrapInstrumentation()) {
                    newClassBuilder = newClassBuilder.method(isStatic().and(staticMethodsInterceptPoint.getMethodsMatcher()))
                                                     .intercept(MethodDelegation.withDefaultConfiguration()
                                                                                .withBinders(Morph.Binder.install(OverrideCallable.class))
                                                                                .to(BootstrapInstrumentBoost.forInternalDelegateClass(interceptor)));
                } else {
                    // 先看这里
                    // -- 增强构建器
                    newClassBuilder = newClassBuilder.method(
                        // 静态
                        isStatic()
                        // 拿到各个拦截类的matcher。eg：ConnectionImplCreateInstrumentation 中返回named(CONNECT_METHOD).and(takesArguments(1))
                        .and(staticMethodsInterceptPoint.getMethodsMatcher()))
                        // 创建一个方法委派的默认定义
                        .intercept(MethodDelegation.withDefaultConfiguration()
                        // 绑定一个方法委派的请求参数类型 OverrideCallable  （后面是表示原来方法调用，用法：overrideCallable.call()）
                        .withBinders(Morph.Binder.install(OverrideCallable.class))
                        // 声明具体的拦截实现类 StaticMethodsInterWithOverrideArgs，里面定义了intercept()方法
                        .to(new StaticMethodsInterWithOverrideArgs(interceptor)));
                }
            } else {
                if (isBootstrapInstrumentation()) {
                    newClassBuilder = newClassBuilder.method(isStatic().and(staticMethodsInterceptPoint.getMethodsMatcher()))
                                                     .intercept(MethodDelegation.withDefaultConfiguration()
                                                                                .to(BootstrapInstrumentBoost.forInternalDelegateClass(interceptor)));
                } else {
                    // 由于所有的实现类isOverrideArgs==false&&isBootstrapInstrumentation()==false
                    // 所以基本走到这一步
                    newClassBuilder = newClassBuilder.method(
                        // 静态
                        isStatic()
                        // 拿到各个拦截类的matcher。eg：ConnectionImplCreateInstrumentation 中返回named(CONNECT_METHOD).and(takesArguments(1))
                        .and(staticMethodsInterceptPoint.getMethodsMatcher()))
                        // 创建一个方法委派的默认定义
                        .intercept(MethodDelegation.withDefaultConfiguration()
                        // 声明具体的拦截实现类 StaticMethodsInter，里面定义了intercept()方法
                        .to(new StaticMethodsInter(interceptor)));
                }
            }
        }
        // 返回增强后的构建器
        return newClassBuilder;
    }
}
